﻿using FPSO.Models.Constant;
using FPSO.Models.Domain;
using FPSO.Models.TagMapping;
using NSubstitute.Core;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace FPSO.Services.Utils
{
    public class TSMapperBrige
    {
        private List<TagProp> tagList;
        private Dictionary<long, List<TagProp>> tagDic;
        private Dictionary<string, List<TagProp>> tagnameDic;
        private object mainContainer;
        private ITSService tsService;

        public string IntTable { get; set; }
        public string Separator { get; set; }

        public TSMapperBrige(ITSService service)
        {
            Separator = ".";
            IntTable = "tab_int";
            tsService = service;
            tagList = new List<TagProp>();
            tagDic = new Dictionary<long, List<TagProp>>();
            tagnameDic = new Dictionary<string, List<TagProp>>();
            mainContainer = null;
        }

		//DEBUG 这里增加直接从tagList_tankStatus，tagList_valveStatus，tagList_pumpStatus获取数值
		public void UpdateValues_tagList()
		{
			List<OPCTag> pumpList = new List<OPCTag>();
			if (TankConstant.tagList_pumpStatus != null)
				pumpList.InsertRange(0, TankConstant.tagList_pumpStatus);

			List<OPCTag> tankList = new List<OPCTag>();
			if (TankConstant.tagList_tankStatus != null)
				tankList.InsertRange(0, TankConstant.tagList_tankStatus);

			List<OPCTag> valueList = new List<OPCTag>();
			if (TankConstant.tagList_valveStatus != null)
				valueList.InsertRange(valueList.Count, TankConstant.tagList_valveStatus);

			string output = string.Empty;
			foreach (OPCTag taginf in tankList)
			{
				try
				{
					string tagname0 = taginf.DispName.ToUpper();
					string tagname = tagname0.Replace('-', '_');

					//DEBUG 阀门的SCM_OPEN和SCM_CLOSE暂时都无法响应，与设置的响应状态OPENCMD和CLOSECMD不匹配
					if (!tagname.Contains("LEVEL") && !tagname.Contains("DRAFT") && !tagname.Contains("TEMP") && !tagname.Contains("PRESS"))
						continue;

					if (tagname.Contains("BOTTOM"))
					{
						double a = 0;
					}

					List<TagProp> propList = tagnameDic[tagname];
					if (taginf.OpcValue != null)
					{
						double value = Convert.ToDouble(taginf.OpcValue.Value);
						foreach (TagProp prop in propList)
						{
							if (prop.TagName.IndexOf("DRAFT") > 0 || prop.TagName.IndexOf("LEVEL") > 0 || prop.TagName.IndexOf("PRESS") > 0 || prop.TagName.IndexOf("TEMP") > 0)
							{
								//DEBUG 检查特定阀门的响应状态
								if ((tagname.Contains("COT") || tagname.Contains("WBT")) && value > 0)
								{
									output += string.Format("{0} ：{1} // ", tagname, value);
								}
								prop.SetValue(value, true);
								continue;
							}
						}
					}
				}
				catch (Exception ex)
				{
				}
			}
			//if (TankConstant.g_Debug)
			{
				TankConstant.MyLog.appendText(output);
			}

			foreach (OPCTag taginf in valueList)
			{
				try
				{
					string tagname0 = taginf.DispName.ToUpper();
					string tagname = tagname0.Replace('-', '_');

					//DEBUG 检查特定阀门的响应状态
					if (tagname.Contains("96010"))
					{
						double testa = 0;
					}

                    //DEBUG 阀门的SCM_OPEN和SCM_CLOSE暂时都无法响应，与设置的响应状态OPENCMD和CLOSECMD不匹配
                    //对VALVE POSITION开度阀的显示操作也是临时的
                    if (!tagname.Contains("STATUS") && !tagname.Contains("POSITION"))
						continue;
                    if(tagname.Contains("POSITION"))
                    {
                        tagname = tagname.Replace(" VALVE POSITION", ".OPENRATIO").Trim();
                    }
					List<TagProp> propList = tagnameDic[tagname];
					if(taginf.OpcValue != null)
					{
						double value = Convert.ToDouble(taginf.OpcValue.Value);
						foreach (TagProp prop in propList)
						{
							//阀门通讯数据刷新
							if (prop.TagName.IndexOf("STATUSOPENED") > 0 || prop.TagName.IndexOf("STATUSCLOSED") > 0 || prop.TagName.IndexOf("OPENRATIO") > 0)
							{
								prop.SetValue(value, true);
								continue;
							}
                        }
					}
				}
				catch(Exception ex) 
				{
				}
			}

			foreach (OPCTag taginf in pumpList)
			{
				try
				{
					//DEBUG 这里泵的命名方式后面要统一
					string tagname0 = taginf.DispName.ToUpper();
					if (!tagname0.Contains("PUMP") || !tagname0.Contains("CARGO OIL SYSTEM HYDR") || !tagname0.Contains("FEEDBACK"))
						continue;
					string[] strs = tagname0.Split('_');
					string pumpname = strs[1];

					if (taginf.OpcValue != null)
					{
						int value = Convert.ToInt16(taginf.OpcValue.Value);
						if(value > 0)
						{
							double a = 0;
						}
						string tagname = string.Format("HULL_P_{0}.RUNNING", pumpname);
						List<TagProp> propList = tagnameDic[tagname];
						if(propList != null)
						{
							foreach (TagProp prop in propList)
							{
								if (value > 0)
									prop.SetValue(value, true);
								else
									prop.SetValue(value, false);
								continue;
							}
						}
					}
				}
				catch (Exception ex)
				{
				}
			}
		}

		public void UpdateValues()
        {
			List<TagValue> valueList = tsService.GetIntValues(IntTable, tagDic.Keys.ToList());

			foreach (TagValue val in valueList)
            {
				List<TagProp> propList = tagDic[val.TagId];
                foreach (TagProp prop in propList)
                {
                    //液舱通讯数据刷新

                    //DEBUG 用于测试通讯
                    if (prop.TagName.Contains("91008"))
                    {
                        double testa = 0;
                    }
                    if (prop.TagName.IndexOf("LEVEL") > 0 || prop.TagName.IndexOf("TEMP") > 0 || prop.TagName.IndexOf("PRESS") > 0)
                    {
                        prop.SetValue(val);
                        continue;
                    }
					//阀门通讯数据刷新
					else if(prop.TagName.IndexOf("OPENCMD") > 0 || prop.TagName.IndexOf("CLOSECMD") > 0 || prop.TagName.IndexOf("STATUSOPENED") > 0
						|| prop.TagName.IndexOf("STATUSCLOSED") > 0 || prop.TagName.IndexOf("STATUSFAULT") > 0)
					{
						prop.SetValue(val);
						continue;
					}
                    else if(prop.TagName.IndexOf("DRAFT") > 0)
                    {
                        prop.SetValue(val);
                        continue;
                    }
                }
            }
        }
        //这里不传参是通过pinus数据库为界面赋值，此时pinus数据来自OPC，即仅对通讯相关控件赋值

        //沈大师注意，现在控件的赋值顺序是先取pinus里的值（bindingService.ReflushData(DateTime.Now);）
        //再将计算值赋至控件executeView.SetTankStatus(calcResult);
        //但是在ExecuteView的private void TaskTimer_Tick(object sender, EventArgs e)中会再次执行oilBallast.SetImitateTime，此时又会获得pinus的值
        //这里需要先将pinus中对应的字段屏蔽掉，或者直接将pinus对应字段删除

        //这里传参的UpdateValues是通过calcResult中的各舱数据为界面对应舱室控件赋值，这些值来自于后端计算反馈
        //沈大师注意，这里字段的选择要写的相对灵活点，可以和张权沟通，他现在写的有点垃圾
        public void UpdateValues(ManualCalcResult calcResult)
        {
            //待测试，控件的赋值需要依赖于PDB字段名，所以PDB里需要建立所有字段，除绑定控件通过OPC读取外，其余可以不赋值，仅用于控件检索
            //但是这里如果分别取值会有精度换算问题
            List<string> tagNameList = tagList.Select(t => t.TagName).Distinct().ToList();
			//DEBUG 转换成不依赖pinus的方式
			List<TagInfo> tagInfoList = tsService.GetTagInfo(tagNameList);
			//List<TagInfo> tagInfoList = tsService.GetTagInfo(IntTable, tagNameList);
            foreach (TagInfo taginf in tagInfoList)
            {
				List<TagProp> propList = tagnameDic[taginf.TagName];
				//List<TagProp> propList = tagDic[taginf.TagID];
				foreach (TagProp prop in propList)
                {
                    string tagname = prop.TagName;
                    int pos = tagname.IndexOf('.');
                    string tankname = tagname.Substring(0, pos);
                    string param = tagname.Substring(pos + 1, tagname.Length - pos - 1).ToUpper();

					//如果是温度或者压力，这里不再重新复制，后面应该将温度和压力也传到后端参与计算，之后统一返回
					if (param.IndexOf("TEMP") > -1 || param.IndexOf("PRESS") > -1)
						continue;

                    TankCalcState tankCalcState = calcResult.GetTankCalcStateByName(tankname);
                    if (tankCalcState == null)
                        continue;
					//DEBUG 测试程序
                    if (tankname.Contains("OFFSPEC"))
                    {
                        double a = 0;
                    }
                    if (tankname == tankCalcState.Abname)
                    {
                        double value = 0;
                        if (param == "VOLM")
                            value = tankCalcState.Volume;
                        else if (param == "LEVEL")
                            value = tankCalcState.Inn;
                        else if (param == "RATE")
                            value = tankCalcState.Rate;
                        else if (param == "DENSITY")
                            value = tankCalcState.Density;
                        else if (param == "FULL")
                            value = tankCalcState.Full;
                        prop.SetValue(value,false);
                        continue;
                    }
                }
            }
            //List<TagValue> valueList = tsService.GetIntValues(IntTable, tagDic.Keys.ToList());
            //foreach (TagValue val in valueList)
            //{
            //    List<TagProp> propList = tagDic[val.TagId];
            //    foreach (TagProp prop in propList)
            //    {
            //        string tagname = prop.TagName;
            //        int pos = tagname.IndexOf('.');
            //        string tankname = tagname.Substring(0,pos);
            //        string param = tagname.Substring(pos + 1, tagname.Length - pos - 1).ToUpper();
            //        TankCalcState tankCalcState = calcResult.GetTankCalcStateByName(tankname);
            //        if (tankname == tankCalcState.Abname)
            //        {
            //            double value = 0;
            //            if (param == "VOLUME")
            //                value = tankCalcState.Volume;
            //            else if(param == "LEVEL")
            //                value = tankCalcState.Inn;
            //            else if (param == "RATE")
            //                value = tankCalcState.Rate;
            //            else if (param == "DENSITY")
            //                value = tankCalcState.Density;
            //            else if(param == "FULL")
            //                value = tankCalcState.Full;
            //            prop.SetValue(value);
            //            continue;
            //        }
            //    }
            //}
            //TankCalcState no1_wbt_p = calcResult.NO1_WBT_P;
            //string name = "NO1_WBT_P.VOLM";
            //List<TagProp> propList = tagnameDic[name];
            //foreach (TagProp prop in propList)
            //{ 
            //    double val = no1_wbt_p.Volume;
            //    prop.SetValue(val);
            //    double a = 0;
            //}
        }

		public void InitDataContext(object dataContext)
        {
            if (mainContainer != null)
            {
                throw new Exception("TSMapperBrige对象已经初始化");
            }

			//NOTICE 界面控件和数据绑定初始化
            tagList.Clear();
            tagDic.Clear();
            tagnameDic.Clear();
            PickUpTagInfo(dataContext, string.Empty);

            List<string> tagNameList = tagList.Select(t => t.TagName).Distinct().ToList();
			//DEBUG 转换成不依赖pinus的方式
			List<TagInfo> tagInfoList = tsService.GetTagInfo(tagNameList);
			//List<TagInfo> tagInfoList = tsService.GetTagInfo(IntTable, tagNameList);

            Dictionary<string, TagInfo> tagInfoDic = new Dictionary<string, TagInfo>();
            foreach (TagInfo t in tagInfoList)
            {
                tagInfoDic.Add(t.TagName, t);
            }

            foreach (TagProp tagProp in tagList)
            {
                if (tagInfoDic.ContainsKey(tagProp.TagName))
                {
                    TagInfo tagInfo = tagInfoDic[tagProp.TagName];
					//DEBUG 转换成不依赖pinus的方式
					tagProp.TagID = tagInfo.TagID;
					if (!tagnameDic.ContainsKey(tagProp.TagName))
					{
						tagDic.Add(tagProp.TagID, new List<TagProp>() { tagProp });
						tagnameDic.Add(tagProp.TagName, new List<TagProp>() { tagProp });
					}
					else
					{
						tagDic[tagProp.TagID].Add(tagProp);
						tagnameDic[tagProp.TagName].Add(tagProp);
					}
					//tagProp.TagID = tagInfo.TagID;
					//if (!tagDic.ContainsKey(tagProp.TagID))
					//{
					//	tagDic.Add(tagProp.TagID, new List<TagProp>() { tagProp });
					//	tagnameDic.Add(tagProp.TagName, new List<TagProp>() { tagProp });
					//}
					//else
					//{
					//	tagDic[tagProp.TagID].Add(tagProp);
					//	tagnameDic[tagProp.TagName].Add(tagProp);
					//}
				}
            }

            mainContainer = dataContext;
        }

        private void PickUpTagInfo(object obj, string prefix)
        {
            Type type = obj.GetType();
            PropertyInfo[] props = type.GetProperties();
            foreach (var prop in props)
            {
                if (prop.IsDefined(typeof(FieldMapperAttribute), false))
                {
                    FieldMapperAttribute fieldMapper =
                        (FieldMapperAttribute)prop.GetCustomAttribute(typeof(FieldMapperAttribute), false);
                    string mapperName = fieldMapper.FieldMapperName;
                    string pathName = string.Format("{0}{1}{2}", prefix, Separator, mapperName);
                    if (string.IsNullOrEmpty(prefix))
                    {
                        pathName = mapperName;
                    }

                    TypeCode targetCode = Type.GetTypeCode(prop.PropertyType);
                    if (targetCode == TypeCode.Object)
                    {
                        PickUpTagInfo(prop.GetValue(obj), pathName);
                    }
                    else if (fieldMapper.IsTStamp)
                    {
                        if (targetCode != TypeCode.DateTime)
                        {
                            throw new Exception("初始化标签信息失败:类型不匹配!");
                        }
                        tagList.Add(new TagProp(pathName, prop, obj, fieldMapper));
                    }
                    else if (fieldMapper.SourceType == TypeCode.Int32)
                    {
                        if (targetCode != TypeCode.Int32
                            && targetCode != TypeCode.Int64
                            && targetCode != TypeCode.Double
                            && targetCode != TypeCode.Boolean)
                        {
                            throw new Exception("初始化标签信息失败:类型不匹配");
                        }
                        tagList.Add(new TagProp(pathName, prop, obj, fieldMapper));
                    }
                    else if (fieldMapper.SourceType == TypeCode.Empty && targetCode == TypeCode.Int32)
                    {
                        //没指定数据源类型的情况下，目标类型是int
                        tagList.Add(new TagProp(pathName, prop, obj, fieldMapper));
                    }
                    else
                    {
                        throw new Exception("初始化标签信息失败：类型转换失败！");
                    }
                }
            }
        }
    }
}
